package com.mjk.common.core.jdbc.service.plus.idbservice.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.page.PageMethod;

import com.mjk.common.base.model.BaseEntity;
import com.mjk.common.base.model.BaseExample;
import com.mjk.common.core.jdbc.annotation.ArcSharding;
import com.mjk.common.core.jdbc.annotation.Id;
import com.mjk.common.core.jdbc.bean.BeePage;
import com.mjk.common.core.jdbc.datasource.DynamicDataSource;
import com.mjk.common.core.jdbc.service.plus.idbservice.BaseDBService;
import com.mjk.common.core.jdbc.service.plus.mapper.ExampleMapper;
import com.mjk.common.core.jdbc.service.plus.provider.BeeDataSourceType;
import com.mjk.common.core.jdbc.util.TableUtils;
import com.mjk.common.core.jdbc.util.provider.SelectSqlProvider;
import com.mjk.common.core.jdbc.util.provider.SqlProvider;
import com.mjk.common.tools.gson.GsonUtils;
import com.mjk.common.tools.lambada.LambdaReflectionUtil;
import com.mjk.common.util.*;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @param <T>
 * @param <K>
 */
@Service
public class BaseDBServiceImpl<T extends BaseEntity, K extends BaseExample>
        implements BaseDBService<T, K>,ApplicationListener<ContextRefreshedEvent>{
	private Map<String,Object> mappers;
    @Autowired
    private ExampleMapper baseMapper;
    
	@Value("${sharding.jdbc.datasource.defaultname:his}")
	private String defaltSource;

	private  String exampleFiled = "example";

	private  String selectByExample = "selectByExample";
    /**
     * 统一获取mapper 必须要命名规范 beanName+Mapper 的命名才行
     * @param t
     * @return
     */
    public Object getMapper(BaseEntity t){
    	String className = t.getClass().getName()+"Mapper";    	
        ArcSharding sharding = t.getClass().getAnnotation(ArcSharding.class);
        if(null!=sharding&& ObjectUtils.isNotEmpty(sharding.dataSource())){
            DynamicDataSource.setDatabaseType(sharding.dataSource());
        }else {//如果没有配置 默认为defaultname
        	DynamicDataSource.setDatabaseType(defaltSource);
        }
        if(null!=mappers && mappers.containsKey(className)) {
        	return mappers.get(className);
        }
        if(null!=getMapper()) {
        	return getMapper();
        }
        return baseMapper;
    }
    

    public Object getMapper(){
    	return null;
    }

    /**
     * 更具条件返回 条数
     * @param example
     * @return
     */
    @Override
    public Integer countByExample(K example) throws RuntimeException{
    	if(BeeDataSourceType.MONGODB_WRAP.getDriver().equals(TableUtils.getDriverName(example))){
    		Map<String,Object> map = new HashMap<>();
    		map.put(exampleFiled, example);
    		return Integer.valueOf(TableUtils.getHandler(example).countByExample(map));
    	}else {
            Object o = LambdaReflectionUtil.invoke(getMapper(SqlProvider.getEntityFromExample(example)), "countByExample", example);
	        //Object o = ReflectionUtils.invokeMethod(getMapper(SqlProviderUtils.getEntityFromExample(example)), "countByExample", new Class[]{BaseExample.class}, new Object[]{example});
	        if (o != null) {
	            return NumberUtils.intValue(o.toString());
	        }
	        return Integer.valueOf(0);
    	}
    }

    /**
     * 获取所有
     * @param t
     * @return
     */
    @Override
    public List<T> getAll(T t) {
        BaseExample example = SqlProvider.createExample(t);
        Object o =LambdaReflectionUtil.invoke(getMapper(t), "selectByExample",  example);
        //Object o = ReflectionUtils.invokeMethod(getMapper(t), selectByExample, new Class[]{BaseExample.class}, new Object[]{example});
        if (o != null) {
			return (List<T>) SqlProvider.getListFromListMap(o, SqlProvider.getEntityFromExample(example));
        }
        return new ArrayList<>();
    }

    @Override
    public List<T> findManyByExample(K example) {
    	if(BeeDataSourceType.MONGODB_WRAP.getDriver().equals(TableUtils.getDriverName(example))){
    		Map<String,Object> map = new HashMap<>();
    		map.put(exampleFiled, example);
            return (List<T>) GsonUtils.json2List(TableUtils.getHandler(example).selectByExample(map), SqlProvider.getEntityFromExample(example).getClass());
    	}else {
            Object o = LambdaReflectionUtil.invoke(getMapper(SqlProvider.getEntityFromExample(example)), "selectByExample", example);
	        if (o != null) {
				return (List<T>) SqlProvider.getListFromListMap(o, SqlProvider.getEntityFromExample(example));
	        }
	        return new ArrayList<>();
    	}
    }
    
    @Override
    public T findOneByExample(K example) {
        Object o = LambdaReflectionUtil.invoke(getMapper(SqlProvider.getEntityFromExample(example)), "selectByExample", example);
        //Object o = ReflectionUtils.invokeMethod(getMapper(SqlProviderUtils.getEntityFromExample(example)), selectByExample, new Class[]{BaseExample.class}, new Object[]{example});
        if (o != null) {
			List list = SqlProvider.getListFromListMap(o, SqlProvider.getEntityFromExample(example));
            if (!CollectionUtils.isEmpty(list)) {
                return (T) list.get(0);
            }
            return null;
        }
        return null;
    }
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
	public BeePage<T> findManyByExampleWithPage(K example) {
    	if (ReflectionUtils.getFieldValue(example, "start") != null
				&& ReflectionUtils.getFieldValue(example, "limit") != null) {
    		int p = Integer.parseInt(ReflectionUtils.getFieldValue(example, "start").toString());
			int r = Integer.parseInt(ReflectionUtils.getFieldValue(example, "limit").toString());
			String orderBy = (String)ReflectionUtils.getFieldValue(example, "orderByClause");
			Page page;
			if(ObjectUtils.isNotEmpty(orderBy)) {
				 page = PageMethod.startPage(p, r,orderBy);
			}else {
				 page = PageMethod.startPage(p, r);
			}
            LambdaReflectionUtil.invoke(getMapper(SqlProvider.getEntityFromExample(example)), "selectByExample", example);
	        //ReflectionUtils.invokeMethod(getMapper(SqlProviderUtils.getEntityFromExample(example)), selectByExample, new Class[]{BaseExample.class}, new Object[]{example});
	        if (page != null) {
  	            return new BeePage<>(page,example);
  	        }
	        return new BeePage(new ArrayList<>());
    	}else{
    		return new BeePage(findManyByExample(example));
    	}
    
    }
    @Override
    public T findOne(T t) {
        BaseExample example = SqlProvider.createExample(t);
        Object o = LambdaReflectionUtil.invoke(getMapper(t), "selectByExample",  example);
        //Object o = ReflectionUtils.invokeMethod(getMapper(t), selectByExample, new Class[]{BaseExample.class}, new Object[]{example});
        if (o != null) {
			List list = SqlProvider.getListFromListMap(o, SqlProvider.getEntityFromExample(example));
            if (!CollectionUtils.isEmpty(list)) {
                return (T) list.get(0);
            }
            return null;
        }

        return null;
    }


    @Override
    public void save(T t) throws RuntimeException{
    	if(BeeDataSourceType.MONGODB_WRAP.getDriver().equals(TableUtils.getDriverName(t))){
    		Map<String,Object> map = new HashMap<>();
    		map.put("dto", t);
            TableUtils.getHandler(t).insert(map);
        }else {
    		Object o = LambdaReflectionUtil.invoke(getMapper(t), "insertSelective",  t);
	    	//Object o = ReflectionUtils.invokeMethod(getMapper(t), "insertSelective", new Class[]{BaseEntity.class}, new Object[]{t});
	    	if (o != null) {
                NumberUtils.intValue(o.toString());
            }
        }
    }

    
    
    @Override
	public Integer insert(T t) {
    	if(BeeDataSourceType.MONGODB_WRAP.getDriver().equals(TableUtils.getDriverName(t))){
    		Map<String,Object> map = new HashMap<>();
    		map.put("dto", t);
    		return Integer.valueOf(TableUtils.getHandler(t).insert(map));
    	}else {
	    	String keyName = "";
			for(Field field : t.getClass().getDeclaredFields()) {
				Id id = field.getAnnotation(Id.class);
				if(ObjectUtils.isNotEmpty(id)) {
					keyName = field.getName();
					break;
				}
			}
	    	Object o1 =ReflectionUtils.getFieldValue(getMapper(t), "h");
	    	if(ObjectUtils.isEmpty(ReflectionUtils.getField(o1.getClass(), "mapperInterface"))){
				Object target = AopTargetUtils.getTarget(getMapper(t));
				o1 =ReflectionUtils.getFieldValue(target, "h");				
			}
	    	Object o2=ReflectionUtils.getFieldValue(o1, "sqlSession");
	    	SqlSessionFactory o3=(SqlSessionFactory) ReflectionUtils.getFieldValue(o2, "sqlSessionFactory");
	    	Configuration cf = o3.getConfiguration();
	    	MappedStatement ms =cf.getMappedStatement("com.mjk.common.core.jdbc.service.plus.mapper.ExampleMapper.insertSelectivewithId");
	    	String[] keys = new String[] {keyName};
	    	ReflectionUtils.setFieldValue(ms, "keyColumns", keys);
	    	ReflectionUtils.setFieldValue(ms, "keyProperties",keys);
	    	Object o = LambdaReflectionUtil.invoke(getMapper(t), "insertSelectivewithId",  t);
	    	//Object o = ReflectionUtils.invokeMethod(getMapper(t), "insertSelectivewithId", new Class[]{BaseEntity.class}, new Object[]{t});
	    	if (o != null) {
	            return NumberUtils.intValue(o.toString());
	        }
	        return Integer.valueOf(0);
    	}
	}
    
    @Override
    public Integer update(T t) throws RuntimeException{
    	Object o = LambdaReflectionUtil.invoke(getMapper(t), "updateByPrimaryKeySelective",  t);
    	//Object o = ReflectionUtils.invokeMethod(getMapper(t), "updateByPrimaryKeySelective", new Class[]{BaseEntity.class}, new Object[]{t});
    	if (o != null) {
            return NumberUtils.intValue(o.toString());
        }
        return Integer.valueOf(0);
    }
    
    @Override
    public  Integer update(T paramT,K paramK) throws RuntimeException{
    	if(BeeDataSourceType.MONGODB_WRAP.getDriver().equals(TableUtils.getDriverName(paramT))){
    		Map<String,Object> map = new HashMap<>();
    		map.put(exampleFiled, paramK);
    		map.put("dto", paramT);
    		return Integer.valueOf(TableUtils.getHandler(paramT).updateByExampleSelective(map));
    	}else {
	    	Object o = ReflectionUtils.invoke(getMapper(paramT), "updateByExampleSelective", new Class[]{BaseEntity.class,BaseExample.class}, new Object[]{paramT,paramK});
	    	if (o != null) {
	            return NumberUtils.intValue(o.toString());
	        }
	        return Integer.valueOf(0);
    	}
    }
    @Override
    public void save(List<T> paramList) {
    	ReflectionUtils.invoke(getMapper(paramList.get(0)), "insertList", new Class[]{List.class}, new Object[]{paramList});
    }


    @Override
    public void update(List<T> paramList) {
		LambdaReflectionUtil.invoke(getMapper(paramList.get(0)), "updateList", paramList);
       }

    @Override
    public Integer delete(T t) throws RuntimeException{
    	Object o = LambdaReflectionUtil.invoke(getMapper(t), "deleteByPrimaryKey",  t);
    	//Object o = ReflectionUtils.invokeMethod(getMapper(t), "deleteByPrimaryKey", new Class[]{BaseEntity.class}, new Object[]{t});
    	if (o != null) {
            return NumberUtils.intValue(o.toString());
        }
        return Integer.valueOf(0);
    }
    
    @Override
    public Integer delete(K t) throws RuntimeException{
    	if(BeeDataSourceType.MONGODB_WRAP.getDriver().equals(TableUtils.getDriverName(t))){
    		Map<String,Object> map = new HashMap<>();
    		map.put(exampleFiled, t);
    		return Integer.valueOf(TableUtils.getHandler(t).deleteByExample(map));
    	}else {
            Object o = LambdaReflectionUtil.invoke(getMapper(SqlProvider.getEntityFromExample(t)), "deleteByExample", t);
//	    	Object o = ReflectionUtils.invokeMethod(getMapper(SqlProviderUtils.getEntityFromExample(t)), "deleteByExample", new Class[]{BaseExample.class}, new Object[]{t});
	    	if (o != null) {
	            return NumberUtils.intValue(o.toString());
	        }
	        return Integer.valueOf(0);
    	}
    }
    
    @Override
    public void delete(List<T> paramList) {
    	LambdaReflectionUtil.invoke(getMapper(paramList.get(0)), "deleteList",  paramList);
    	//ReflectionUtils.invokeMethod(getMapper(paramList.get(0)), "deleteList", new Class[]{List.class}, new Object[]{paramList});
    }

    @SuppressWarnings("unchecked")
	@Override
    public List<Map<String,Object>> excuteSql(Map<String,Object> sql) {
    	Object o = LambdaReflectionUtil.invoke(baseMapper, "excuteSql", sql);
//    	Object o = ReflectionUtils.invokeMethod(baseMapper, "excuteSql", new Class[]{Map.class}, new Object[]{sql});
    	if(null!=o){
    		return (List<Map<String, Object>>) o;
    	}
    	return new ArrayList<>();
    }

	@Override
	public List<Map<String, Object>> excuteSql(String paramString) {
		Map<String,Object> sql = new HashMap<>();
		sql.put("sql", paramString);
		return excuteSql(sql);
	}

	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		mappers = event.getApplicationContext().getBeansWithAnnotation(Mapper.class);
	}


	

}
